﻿using System;
using System.Collections.Generic;

namespace TC
{
    public interface IPopChild
    {
    }

    public interface IRepeat
    {
        void Next(int status);
    }

    public interface IBack
    {
        void Back();
    }
    
    public abstract class FlowNode : Node
    {
        public Dispatcher chain;

#if UNITY_EDITOR
        public bool HasBreak = false;
#endif

        public Dictionary<string, Port> inputPorts = new Dictionary<string, Port>();
        public Dictionary<string, Port> outputPorts = new Dictionary<string, Port>();

        public virtual void Execute(Dispatcher chain) { this.chain = chain; }
        public virtual void Stop() { }
        
        public override ISerializeEntity CreateEntity()
        {
            return new FlowNode_entity();
        }

        public override void OnSerialize_t(ISerializeEntity entity)
        {
            base.OnSerialize_t(entity);

            FlowNode_entity f = entity as FlowNode_entity;
            f.hasBreak = HasBreak;
            foreach (var i in inputPorts)
            {
                ISerializeEntity port = i.Value.CreateEntity();
                if(port != null)
                {
                    i.Value.OnSerialize_t(port);
                    f.inports.Add(port as Port_entity);
                }
            }

            foreach(var i in outputPorts)
            {
                ISerializeEntity port = i.Value.CreateEntity();
                if(port != null)
                {
                    i.Value.OnSerialize_t(port);
                    f.outports.Add(port as Port_entity);
                }
            }
        }

        public override void OnDeserialize_t(ISerializeEntity entity)
        {
            base.OnDeserialize_t(entity);

            FlowNode_entity f = entity as FlowNode_entity;
            HasBreak = f.hasBreak;

            foreach(var i in f.inports)
            {
                Port_entity p = i as Port_entity;
                inputPorts[p.name].OnDeserialize_t(p);
            }

            foreach(var i in f.outports)
            {
                Port_entity p = i as Port_entity;
                outputPorts[p.name].OnDeserialize_t(p);
            }
        }

        #region interface
        public void AddPort(Port port)
        {
            if (port.IsInput)
            {
                inputPorts.Add(port.name, port);
            }
            else
            {
                outputPorts.Add(port.name, port);
            }
        }

        public FlowInput<T> AddFlowInPort<T>(string name)
        {
            FlowInput<T> flowInput = new FlowInput<T>(this, name);
            inputPorts.Add(name, flowInput);
            return flowInput;
        }

        public FlowOutput<T> AddFlowOutPort<T>(string name)
        {
            FlowOutput<T> flowOutput = new FlowOutput<T>(this, name);
            outputPorts.Add(name, flowOutput);
            return flowOutput;
        }
        
        public ValueInput<T> AddValueInPort<T>(string name)
        {
            ValueInput<T> valueIn = new ValueInput<T>(this, name);
            inputPorts.Add(name, valueIn);
            return valueIn;
        }

        public ValueOutput<T> AddValueOutPort<T>(string name, Func<T> getter)
        {
            ValueOutput<T> valueOut = new ValueOutput<T>(this, name, getter);
            outputPorts.Add(name, valueOut);
            return valueOut;
        }

        public Port AddPort(Type portType, string name)
        {
            Port port = Activator.CreateInstance(portType, this, name) as Port;
            if (port.IsInput){ inputPorts.Add(name, port); }
            else { outputPorts.Add(name, port); }
            return port;
        }
        
        public List<T> GetPorts<T>() where T : Port
        {
            List<T> ls = new List<T>();

            Type t = typeof(T);
            foreach (var i in inputPorts)
            {
                if (i.Value.GetType() == t)
                {
                    ls.Add((T)i.Value);
                }
            }

            foreach (var i in outputPorts)
            {
                if (i.Value.GetType() == t)
                {
                    ls.Add((T)i.Value);
                }
            }

            return ls;
        }
        #endregion
    }
}
